package com.rr.service.impl;

import com.rr.entity.User;
import com.rr.service.TransactionService;
import com.rr.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author RR
 * @date 2023/1/10 13:31
 */

/*
事务简单学习：
REQUIRES_NEW 最为简单，不管当前有无事务，它都会开启一个全新事务，既不影响外部事务，也不会影响其他内部事务，真正的井水不犯河水，坚定而独立。
REQUIRED 在没有外部事务的情况下，会开启一个独立的新事务，且不会对其他同级事务造成影响；而当存在外部事务的情况下，则会与外部事务同生共死。
NESTED 在没有外部事务的情况下与 REQUIRED 效果相同；而当存在外部事务的情况下，当外部事务回滚时，它会创建一个嵌套事务（子事务）。外部事务回滚时，子事务会跟着回滚；但子事务的回滚不会对外部事务和其他同级事务造成影响。
*/
@Slf4j
@Service
public class TransactionServiceImpl implements TransactionService {

    @Autowired
    private UserService userService;

    /*
    * 小水和小镜均成功入库	外部方法未开启事务，所以所有插入操作均未受到外部异常影响
    * */
    @Override
    public void noTransaction_required_required_externalException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequired(xiaoJing);
        throw new RuntimeException();
    }

    /*
    * 小水入库，小镜未入库
    * 外部方法未开启事务，内部方法事务各自独立，互不影响，「小镜」的插入方法发生异常回滚，但「小水」的插入方法不受影响
    * */
    @Override
    public void noTransaction_required_requiredException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredAndException(xiaoJing);
    }

    /*
    * 小水和小镜均未入库
    * 外部方法开启事务，所有内部方法均加入外部方法的事务中。而外部方法发生异常，所以导致所有操作都发生回滚
    * */
    @Override
    @Transactional
    public void transaction_required_required_externalException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequired(xiaoJing);
        throw new RuntimeException();
    }

    /*
    * 小水和小镜均未入库
    * 外部方法开启事务，所有内部方法均加入外部方法的事务中。由于「小镜」的插入方法发生异常，此时所有方法都处于同一个事务中，所以导致所有操作都发生回滚
    * */
    @Override
    @Transactional
    public void transaction_required_requiredException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredAndException(xiaoJing);
    }

    /*
    * 小水和小镜均未入库
    * 外部方法开启事务，所有内部方法均加入外部方法的事务中。由于「小镜」的插入方法发生异常，此时所有方法都处于同一个事务中，即使发生异常的部分被try-catch住，所有操作仍然会回滚
    * */
    @Override
    @Transactional
    public void transaction_required_requiredException_try() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        try {
            userService.addWithRequiredAndException(xiaoJing);
        } catch (Exception e) {
            log.error("发生异常，事务回滚！");
        }
    }

    /*
    * 小水未入库，小镜入库
    * 外部方法开启事务，「小水」的插入方法和外部方法在同一个事务中，跟随外部方法发生回滚；「小镜」的插入方法开启一个独立的新事务，不受外部方法异常的影响
    * */
    @Override
    @Transactional
    public void transaction_required_requiredNew_externalException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredNew(xiaoJing);
        throw new RuntimeException();
    }

    /*
    * 小水未入库，小镜入库，水镜未入库
    * 外部方法开启事务，「水镜」的插入方法开启一个独立的新事务，因为发生异常，所以自己回滚了；「水镜」的异常没有做处理，因此会被外部方法感知到，「小水」的插入方法和外部方法在同一个事务中，跟随外部方法发生回滚；「小镜」的插入方法也会开启一个独立的新事务，因此不会受到任何方法的影响，成功入库
    * */
    @Override
    @Transactional
    public void transaction_required_requiredNew_requiredNewException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        User shuiJing = new User().setName("水镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredNew(xiaoJing);
        userService.addWithRequiredNewAndException(shuiJing);
    }

    /*
    * 小水和小镜入库，水镜未入库
    * 外部方法开启事务，「水镜」的插入方法开启一个独立的新事务，因为发生异常，所以自己回滚了；「水镜」的异常被try-catch处理了，其他方法正常提交「小水」和「小镜」成功入库
    * */
    @Override
    @Transactional
    public void transaction_required_requiredNewException_try() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        User shuiJing = new User().setName("水镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithRequiredNew(xiaoJing);
        try {
            userService.addWithRequiredNewAndException(shuiJing);
        } catch (Exception e) {
            log.error("发生异常，事务回滚！");
        }
    }

    /*
    * 小水和小镜均未入库
    * 外部方法开启事务，内部方法开启各自的子事务，外部方法发生异常，主事务回滚，子事务跟随主事务回滚
    * */
    @Override
    @Transactional
    public void transaction_nested_nested_externalException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithNested(xiaoShui);
        userService.addWithNested(xiaoJing);
        throw new RuntimeException();
    }

    /*
    * 小水和小镜均未入库
    * 外部方法开启事务，内部方法开启各自的子事务，「小镜」的插入方法发生异常回滚自己的子事务；「小镜」的异常没有做处理，因此会被外部方法感知到，「小水」的插入方法在外部方法的子事务中，所以跟随主事务回滚
    * */
    @Override
    @Transactional
    public void transaction_nested_nestedException() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        userService.addWithNested(xiaoShui);
        userService.addWithNestedAndException(xiaoJing);

    }

    /*
    * 小水和小镜入库，水镜未入库
    * 外部方法开启事务，「小镜」和「水镜」开启各自的子事务，「小水」加入外部方法的事务。「水镜」的插入方法发生异常回滚自己的子事务；「水镜」的异常被try-catch处理了，其他方法正常提交「小水」和「小镜」成功入库
    * */
    @Override
    @Transactional
    public void transaction_nested_nestedException_try() {
        User xiaoShui = new User().setName("小水");
        User xiaoJing = new User().setName("小镜");
        User shuiJing = new User().setName("水镜");
        userService.addWithRequired(xiaoShui);
        userService.addWithNested(xiaoJing);
        try {
            userService.addWithNestedAndException(shuiJing);
        } catch (Exception e) {
            log.error("发生异常，事务回滚！",e);
        }
    }
}